package com.qfedu.department.service.impl;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.transaction.Transactional;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.IdentityLinkType;
import org.activiti.engine.task.Task;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;

import com.qfedu.common.entity.WoActivitiTaskLog;
import com.qfedu.common.entity.WoPage;
import com.qfedu.common.util.WoUtil;
import com.qfedu.department.dto.QfLeaveDto;
import com.qfedu.department.po.QfLeave;
import com.qfedu.department.po.Staff;
import com.qfedu.department.repository.QfLeaveRepository;
import com.qfedu.department.repository.StaffRepository;
import com.qfedu.department.service.QfLeaveService;
import com.qfedu.psys.dto.WoUser;

/**
 * PO实体QfLeave对应的Service接口实现类.
 * 
 * @author cailei
 */
@Service
@Transactional
public class QfLeaveServiceImpl implements QfLeaveService {

	/**
	 * 使用日志打印框架.
	 */
	private final static Logger LOG = LogManager.getLogger(QfLeaveServiceImpl.class);

	/**
	 * 注入QfLeaveRepository.
	 */
	@Resource // @Autowired
	private QfLeaveRepository qfLeaveRepository;

	@Resource
	private HistoryService historyService;

	/**
	 * 根据流程实例id和参数名称，获取对应的值.例如，根据申请任务的表单字段名称"applyReason"，获取其对应的值.
	 * 
	 * @param piId
	 * @param variableName
	 * @return
	 */
	private Object getPIVariableValue(String piId, String variableName) {
		HistoricVariableInstance vi = historyService.createHistoricVariableInstanceQuery().processInstanceId(piId)
				.variableName(variableName).singleResult();
		if (vi == null) {
			return null;
		}
		return vi.getValue();
	}

	private String getPIVariableStringValue(String piId, String variableName) {
		Object value = this.getPIVariableValue(piId, variableName);
		if (value == null) {
			return null;
		}
		return value.toString();
	}

	private Long getPIVariableLongValue(String piId, String variableName) {
		Object value = this.getPIVariableValue(piId, variableName);
		if (value == null) {
			return 0L;
		}
		return Long.parseLong(value.toString());
	}

	/**
	 * 列表
	 * 
	 * @param currentUser
	 *            当前用户
	 * @param searchContent
	 *            查询内容文本
	 * @param page
	 *            页索引,从1开始
	 * @param size
	 *            一页的最大记录数
	 * @return
	 */
	@Override
	public WoPage<QfLeaveDto> getPageData(WoUser currentUser, String searchContent, Long page, Long size) {
		// 从dao获取数据
		Pageable pageInput = PageRequest.of(page.intValue() - 1, size.intValue());
		Page<QfLeave> pageData = null;
		if (WoUtil.isEmpty(searchContent)) {
			pageData = qfLeaveRepository.findAll(pageInput);
		} else {
			pageData = qfLeaveRepository.findAllByApplyReasonLike("%" + searchContent + "%", pageInput);
		}
		// 将QfLeave转化为QfLeaveDto
		WoPage<QfLeaveDto> pr = QfLeaveDto.getPageData(pageData.getContent(), pageData.getTotalElements());
		return pr.setPage(page);
	}

	/**
	 * 列表
	 * 
	 * @param currentUser
	 *            当前用户
	 * @param searchContent
	 *            查询内容文本
	 * @param page
	 *            页索引,从1开始
	 * @param size
	 *            一页的最大记录数
	 * @return
	 */
	@Override
	public WoPage<QfLeaveDto> getMyPageData(WoUser currentUser, String searchContent, Long page, Long size) {
		Staff s = staffRepository.findByUserId(currentUser.getId());
		// 创建流程实例查询，条件：流程为千锋请假+和当前用户人员相关+请假原因模糊匹配
		HistoricProcessInstanceQuery q = historyService.createHistoricProcessInstanceQuery()
				.processDefinitionKey(PROC_DEF_KEY).involvedUser(s.getId());
		if (!WoUtil.isEmpty(searchContent)) {
			q.variableValueLike("applyReason", "%" + searchContent + "%");
		}
		// 获取分页数据
		List<HistoricProcessInstance> hpis = q.listPage(Long.valueOf(((page - 1) * size)).intValue(), size.intValue());
		// 构造dto数据
		List<QfLeaveDto> dtos = new ArrayList<QfLeaveDto>();
		for (HistoricProcessInstance hpi : hpis) {
			// 从流程实例的参数中查询表单数据
			QfLeaveDto dto = createDto(hpi);
			dtos.add(dto);
		}
		return new WoPage<QfLeaveDto>(dtos, q.count());
	}

	private QfLeaveDto createDto(HistoricProcessInstance hpi) {
		QfLeaveDto dto = new QfLeaveDto();
		dto.setId(hpi.getId());
		dto.setApplyId(this.getPIVariableStringValue(hpi.getId(), "applyId"));
		dto.setApplyName(this.getPIVariableStringValue(hpi.getId(), "applyName"));
		dto.setApplyReason(this.getPIVariableStringValue(hpi.getId(), "applyReason"));
		dto.setApplyType(this.getPIVariableStringValue(hpi.getId(), "applyType"));
		dto.setApplyHours(this.getPIVariableLongValue(hpi.getId(), "applyHours"));
		dto.setResult(this.getPIVariableStringValue(hpi.getId(), "result"));
		// 获取当前任务key
		Task task = taskService.createTaskQuery().processInstanceId(hpi.getId()).singleResult();
		if (task == null) {
			dto.setStatus("end");
		} else {
			dto.setStatus(task.getTaskDefinitionKey());
		}
		return dto;
	}

	/**
	 * 创建
	 * 
	 * @param currentUser
	 *            当前用户
	 * @param dto
	 */
	@Override
	public void create(WoUser currentUser, QfLeaveDto dto) {
		QfLeave po = dto.createPO();
		qfLeaveRepository.save(po);
	}

	/**
	 * 根据id获取对象
	 * 
	 * @param id
	 * @return
	 */
	@Override
	public QfLeaveDto getById(String id) {
		QfLeave po = qfLeaveRepository.findById(id).get();
		QfLeaveDto dto = new QfLeaveDto(po);
		return dto;
	}

	/**
	 * 更新对象
	 * 
	 * @param dto
	 */
	@Override
	public void update(QfLeaveDto dto) {
		QfLeave po = qfLeaveRepository.findById(dto.getId()).get();
		dto.updatePO(po);
		qfLeaveRepository.save(po);
	}

	/**
	 * 根据ids值删除多个对象
	 * 
	 * @param ids
	 */
	@Override
	public void delete(String[] ids) {
		for (String id : ids) {
			qfLeaveRepository.deleteById(id);
		}
	}

	@Resource
	private RuntimeService runtimeService;

	@Resource
	private TaskService taskService;

	@Resource
	private FormService formService;

	@Resource
	private StaffRepository staffRepository;

	@Override
	public void apply(WoUser currentUser, QfLeaveDto dto) {
		// 1.启动流程实例
		Map<String, Object>vars = new HashMap<String, Object>();
		Staff s = staffRepository.findByUserId(currentUser.getId());
		vars.put("applyId", s.getId());
		ProcessInstance pi = runtimeService.startProcessInstanceByKey(PROC_DEF_KEY, vars);
		// 2.获取当前任务
		Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
		// 设置执行人
		taskService.setAssignee(task.getId(), s.getId());
		// 3.构造当前任务的表单数据
		Map<String, String> form = new HashMap<String, String>();
		form.put("applyId", s.getId());
		form.put("applyName", s.getName());
		form.put("applyReason", dto.getApplyReason());
		form.put("applyHours", dto.getApplyHours().toString());
		form.put("applyType", dto.getApplyType());
		// 4.提交当前任务的表单数据
		formService.submitTaskFormData(task.getId(), form);
		// this.taskService.getIdentityLinksForTask(taskId)
	}

	@Override
	public QfLeaveDto getProcessDataById(WoUser u, String id) {
		// 获取请假流程实例
		HistoricProcessInstance hpi = this.historyService.createHistoricProcessInstanceQuery().processInstanceId(id)
				.singleResult();
		// 通过流程实例获取dto
		QfLeaveDto dto = createDto(hpi);
		// 查询当前任务
		Task t = taskService.createTaskQuery().processInstanceId(id).singleResult();
		// 设置当前用户对应的人员是否为当前任务的候选人
		if (t != null) {
			List<IdentityLink> links = taskService.getIdentityLinksForTask(t.getId());
			Staff s = this.staffRepository.findByUserId(u.getId());
			for (IdentityLink link : links) {
				if (IdentityLinkType.CANDIDATE.equals(link.getType()) && link.getUserId().equals(s.getId())) {
					dto.setCandidate(true);
				}
			}
		}
		// 获取流程实例的任务（包括已经结束和正在运行的任务）
		List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery().processInstanceId(id)
				.list();
		// 按照结束时间逆序
		Collections.sort(tasks, new Comparator<HistoricTaskInstance> () {

			@Override
			public int compare(HistoricTaskInstance o1, HistoricTaskInstance o2) {
				if (o1.getEndTime() == null) {
					return -1;
				}
				return 0 - o1.getEndTime().compareTo(o2.getEndTime());
			}
			
		});
		for (HistoricTaskInstance task : tasks) {
			if (task.getEndTime() == null) {
				continue;
			}
			// 获取某个任务提交表单字段
			List<HistoricDetail> details = historyService.createHistoricDetailQuery().taskId(task.getId()).list();
			WoActivitiTaskLog log = new WoActivitiTaskLog(task, details);
			dto.getLogs().add(log);
		}
		return dto;
	}

	@Override
	public void manager(WoUser currentUser, QfLeaveDto dto) {
		// 查找当前任务：流程实例id+任务定义key
		Task task = taskService.createTaskQuery().processInstanceId(dto.getId()).taskDefinitionKey("taskManager")
				.singleResult();
		// 获取当前用户关联人员数据
		Staff s = staffRepository.findByUserId(currentUser.getId());
		// 设置执行人
		taskService.setAssignee(task.getId(), s.getId());
		//
		long hours = this.getPIVariableLongValue(dto.getId(), "applyHours");
		if (!dto.getManagerResult()) {
			runtimeService.setVariable(task.getExecutionId(), "result", "3");
		} else if (hours <= 4) {
			runtimeService.setVariable(task.getExecutionId(), "result", "2");
		}
		// 构造表单数据
		Map<String, String> map = new HashMap<String, String>();
		map.put("managerResult", dto.getManagerResult().toString());
		map.put("managerRemark", dto.getManagerRemark());
		map.put("managerId", s.getId());
		map.put("managerName", s.getName());
		// 提交当前任务表单数据
		formService.submitTaskFormData(task.getId(), map);
	}

	@Resource
	private ProcessEngineConfiguration processEngineConfiguration;

	@Resource
	private RepositoryService repositoryService;

	@Override
	public ResponseEntity<byte[]> getDiagram(String id) {
		try {
			// 1.header参数设置
			HttpHeaders header = new HttpHeaders();
			header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
			// 2.获取流程图的输入流
			// 2.1.获取当前流程实例的正在运行的activiti实例id
			List<Execution> exes = runtimeService.createExecutionQuery().processInstanceId(id).list();
			List<String> acts = new ArrayList<String>();
			for (Execution e : exes) {
				acts.addAll(runtimeService.getActiveActivityIds(e.getId()));
			}
			// 2.2.获取流程图数据
			String defId = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult()
					.getProcessDefinitionId();
			historyService.createHistoricTaskInstanceQuery().processInstanceId(id).list();
			BpmnModel model = repositoryService.getBpmnModel(defId);
			// 2.3.生成流程图的输入流
			InputStream in = processEngineConfiguration.getProcessDiagramGenerator().generateDiagram(model, "png", acts,
					new ArrayList<String>(), "宋体", "宋体", "宋体", Thread.currentThread().getContextClassLoader(), 1.0);
			// 构造ResponseEntity返回
			ResponseEntity<byte[]> re = new ResponseEntity<byte[]>(FileCopyUtils.copyToByteArray(in), header,
					HttpStatus.OK);
			return re;
		} catch (Exception e) {
			LOG.error("", e);
			return null;
		}
	}

}
